import { Component } from './Component';
import { EventEmitter } from './Observer/EventEmitter';
import {
  registryComponent,
  registryEntityNodeByUUID,
  registryEntityNodeByEntity,
} from './Registry';
import {
  IBasePerformanceData,
  IonPerformanceData,
  PerformanceMonitor,
} from './PerformanceMonitor';

/**
 * EntityNode类：节点是最基础的场景元素容器，具有事件功能，相当于游戏世界的原子单位。需挂载自任意Entity
 * 它用于封装场景中的基本元素，例如角色、道具、UI等。
 * 每个扩展节点可以包含多个组件（Component），并通过组件实现具体的功能。
 *
 * @example
 * ```typescript
 * const node = new EntityNode(world.querySelector("#实体名称")!);
 * node.addComponent(new MovementComponent());
 * ```
 */
export class EntityNode<T = any> extends EventEmitter {
  private _uuid: string = EntityNode.generateUUID();

  /**
   * 获取节点的唯一标识符。
   *
   * @returns 返回节点的UUID。
   */
  get uuid(): string {
    return this._uuid;
  }

  private static generateUUID(): string {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
      // 生成一个0到15之间的随机整数
      const r = (Math.random() * 16) | 0;
      // 如果当前字符是'x'，直接使用生成的随机数，否则根据特定规则生成'y'的替换值
      const v = c === 'x' ? r : (r & 0x3) | 0x8;
      // 将生成的数值转换为十六进制字符串
      return v.toString(16);
    });
  }

  /**
   * 存储组件的 Map，键为组件名称，值为组件实例。
   * @private
   */
  private _components: Map<string, Component<T>> = new Map();

  /**
   * 上次更新的时间戳。
   * @private
   */
  private _lastUpdateDate: number = Date.now();

  /**
   * 关联的游戏实体。
   * @private
   */
  private _entity: T;

  /**
   * 节点是否启用
   * @private
   */
  private _enable: boolean = true;

  /**
   * 缓存的组件数组，避免每次更新时都创建新数组
   * @private
   */
  private _cachedComponentsArray: Component<T>[] = [];

  /**
   * 是否需要刷新组件缓存
   * @private
   */
  private _needsRefreshCache: boolean = true;

  /**
   * 性能监控实例
   * @private
   */
  private _performanceMonitor: PerformanceMonitor = new PerformanceMonitor();

  /**
   * 构造函数，初始化 Node 并关联游戏实体。
   *
   * @param entity - 游戏实体实例。
   */
  constructor(entity: T) {
    super();
    this._entity = entity;
    registryEntityNodeByEntity.set(entity, this);
    registryEntityNodeByUUID.set(this._uuid, this);
  }

  /**
   * 设置节点是否启用
   * @param value - 是否启用
   */
  set enable(value: boolean) {
    if (this._enable === value) {
      console.warn('EntityNode.enable：值与当前值相同');
      return;
    }

    const oldValue = this._enable;
    this._enable = value;

    if (value && !oldValue) {
      // 启用所有组件
      for (const component of this._components.values()) {
        if (component.enable) {
          // @ts-expect-error
          component.onEnable?.();
          // @ts-expect-error
          if (!component.isStart) {
            // @ts-expect-error
            component.start?.();
            // @ts-expect-error
            component.isStart = true;
          }
        }
      }
    } else if (!value && oldValue) {
      // 禁用所有组件
      for (const component of this._components.values()) {
        // @ts-expect-error
        component.onDisable?.();
      }
    }
  }

  /**
   * 获取节点是否启用
   * @returns 节点是否启用
   */
  get enable(): boolean {
    return this._enable;
  }
  /**
   * 获取该节点下所有组件实例
   *
   * 此属性返回一个Map对象，其中包含了当前上下文中存储的所有组件
   *
   * @returns {Map<string, Component<T>>} 返回一个新的Map对象映射，键是组件的标识符，值是组件实例。
   */
  get components(): Map<string, Component<T>> {
    return new Map(this._components);
  }

  /**
   * 获取关联的游戏实体。
   *
   * @returns 返回游戏实体实例。
   */
  get entity(): T {
    return this._entity;
  }

  /**
   * 按权重从小到大排序重新排序组件。
   * @public - 允许组件调用
   */
  protected updateWeights(): void {
    // 使用数组排序后重建Map，提高性能
    const sortedComponentsArray = Array.from(this._components.entries()).sort(
      (a, b) => a[1].weight - b[1].weight
    );
    this._components = new Map(sortedComponentsArray);
    this._needsRefreshCache = true;
  }

  /**
   * 刷新组件。
   */
  private updateFunc(deltaTime: number) {
    if (this._enable) {
      // 只在组件列表变化时重建缓存
      if (
        this._needsRefreshCache ||
        this._cachedComponentsArray.length !== this._components.size
      ) {
        this._cachedComponentsArray = Array.from(this._components.values());
        this._needsRefreshCache = false;
      }

      const isMonitoring = PerformanceMonitor.isMonitoringEnabled;
      try {
        // 先执行所有组件的update
        for (const component of this._cachedComponentsArray) {
          if (!component.enable) {
            continue;
          }
          // @ts-expect-error
          if (component.update) {
            const startTime = isMonitoring ? Date.now() : 0;
            // @ts-expect-error
            component.update(deltaTime);
            if (isMonitoring) {
              this._performanceMonitor.recordComponentPerformance(
                this,
                component,
                startTime,
                Date.now()
              );
            }
          }
        }
      } catch (error) {
        console.error('组件更新错误:', error);
      }
    }
  }

  /**
   * 后刷新组件。
   */
  private lateUpdateFunc(deltaTime: number) {
    if (this._enable) {
      const isMonitoring = PerformanceMonitor.isMonitoringEnabled;
      try {
        // 在所有update执行完后，再执行所有组件的lateUpdate
        for (const component of this._cachedComponentsArray) {
          if (!component.enable) {
            continue;
          }
          // @ts-expect-error
          if (component.lateUpdate) {
            const startTime = isMonitoring ? Date.now() : 0;
            // @ts-expect-error
            component.lateUpdate(deltaTime);
            if (isMonitoring) {
              this._performanceMonitor.recordComponentPerformance(
                this,
                component,
                startTime,
                Date.now()
              );
            }
          }
        }
      } catch (error) {
        console.error('组件更新错误:', error);
      }
    }
  }

  /**
   * 设置是否启用全局性能监控。
   *
   * 当启用时，所有组件的 {@link Component.update} 方法将被性能监控器跟踪，
   * 以便记录和分析其执行性能。关闭此选项可以减少性能开销，适用于生产环境或不需要性能监控的场景。
   *
   * @param isEnable - 布尔值，表示是否启用全局性能监控。默认为开启状态。
   */
  static set isMonitoringEnabled(isEnable: boolean) {
    PerformanceMonitor.isMonitoringEnabled = isEnable;
  }

  /**
   * 设置性能警告事件的回调函数
   *
   * 当性能监控器检测到组件性能警告时，将会调用此回调函数
   *
   * @param event 一个函数，接受IPerformanceData类型的事件数据作为参数，无返回值
   */
  static onPerformanceWarning(event: (event: IonPerformanceData) => void) {
    PerformanceMonitor.onPerformanceWarning = event;
  }

  /**
   * 获取组件的 {@link Component.update} 性能数据
   *
   * 传入的是组件构造函数，它必须继承自 Component 类
   *
   * @param componentName 组件的构造函数
   * @returns 返回 IPerformanceData 类型的数据，如果没有找到对应的组件性能数据，则返回 null
   */
  getComponentPerformance<U extends Component<T>>(
    componentConstructor: new (...args: any[]) => U
  ): IBasePerformanceData | null;

  /**
   * 获取组件的 {@link Component.update} 性能数据
   *
   * @param componentName 组件的名称作为字符串
   * @returns 返回 IPerformanceData 类型的数据，如果没有找到对应的组件性能数据，则返回 null
   */
  getComponentPerformance(componentName: string): IBasePerformanceData | null;

  getComponentPerformance(
    key: string | { new (...args: any[]): Component<T> }
  ) {
    const name = typeof key === 'string' ? key : key.name;

    const component = typeof key === 'string' ? this.getComponent(key) : key;

    return this._performanceMonitor.getComponentPerformance(
      component as Component<T>,
      name
    );
  }

  /**
   * 添加一个组件到 Node。
   *
   * @param componentConstructor - 组件类的构造函数。
   * @param object - 可选的组件初始化属性，默认不修改属性。
   * @returns 返回组件实例，便于链式调用。
   * @template U - 组件类的类型，必须继承自 Component。
   */
  addComponent<U extends Component<T>>(
    componentConstructor: new (...args: any[]) => U,
    object?: Partial<Omit<U, keyof Component<T>>>
  ): this;

  /**
   * 添加一个组件到 Node。
   * 子组件类须加上@apclass修饰器。
   *
   * @param componentName - 组件名称。
   * @param object - 可选的组件初始化属性，默认不修改属性。
   * @returns 返回组件实例，便于链式调用。
   */
  addComponent(componentName: string, object?: Record<string, any>): this;

  addComponent<U extends Component<T>>(
    key: string | { new (...args: any[]): Component<T> },
    object?: Partial<Omit<U, keyof Component<T>>>
  ): this {
    const componentName = typeof key === 'string' ? key : key.name;
    const existingComponent = this.getComponent(componentName);
    if (existingComponent) {
      console.warn(`组件 ${componentName} 已挂载到 EntityNode，请勿二次挂载`);
      return this;
    }
    const componentConstructor =
      typeof key === 'string' ? registryComponent.get(componentName) : key;
    if (!componentConstructor) {
      console.error(`组件 ${componentName} 未找到，无法挂载到 EntityNode`);
      return this;
    }
    const componentNew = new componentConstructor();
    // @ts-expect-error
    componentNew.init(this);
    if (object) {
      Object.assign(componentNew, object);
    }
    // @ts-expect-error
    componentNew.onLoad?.();
    this._components.set(componentName, componentNew);

    this._needsRefreshCache = true;

    if (this._enable && componentNew.enable) {
      // @ts-expect-error
      componentNew.onEnable?.();
      // @ts-expect-error
      componentNew.start?.();

      // @ts-expect-error
      componentNew.isStart = true;
    }

    return this;
  }

  /**
   * 获取指定类型的组件。
   *
   * @param componentName - 组件名称。
   * @returns 返回对应的组件实例。
   */
  getComponent(componentName: string): Component<T> | undefined;

  /**
   * 获取指定类型的组件。
   *
   * @param componentConstructor - 组件类的构造函数。
   * @returns 返回对应的组件实例。
   * @template U - 组件类的类型，必须继承自 Component。
   */
  getComponent<U extends Component<T>>(
    componentConstructor: new (...args: any[]) => U
  ): U | undefined;

  getComponent(
    key: string | { new (...args: any[]): Component<T> }
  ): Component<T> | undefined {
    if (typeof key === 'string') {
      return this._components.get(key);
    } else {
      return this._components.get(key.name);
    }
  }

  /**
   * 获取所有组件。
   *
   * @returns 返回包含所有组件的 Map。
   */
  getComponents(): Map<string, Component<T>> {
    return new Map(this._components);
  }

  /**
   * 移除指定类型的组件。
   *
   * @param componentConstructor - 组件类的构造函数。
   * @returns 如果成功移除返回 true，否则返回 false。
   * @template U - 组件类的类型，必须继承自 Component。
   */
  removeComponent<U extends Component<T>>(componentConstructor: {
    new (...args: any[]): U;
  }): boolean;

  /**
   * 移除指定类型的组件。
   *
   * @param componentName - 组件名称。
   * @returns 如果成功移除返回 true，否则返回 false。
   */
  removeComponent(componentName: string): boolean;

  removeComponent(
    key: string | { new (...args: any[]): Component<T> }
  ): boolean {
    const componentName = typeof key === 'string' ? key : key.name;
    const component = this.getComponent(componentName);
    if (component) {
      // @ts-expect-error
      component._enable = false;
      // @ts-expect-error
      component.onDisable?.();
      // @ts-expect-error
      component.onDestroy?.();
      this._components.delete(componentName);
      this._needsRefreshCache = true;
      return true;
    }
    return false;
  }

  /**
   * 移除所有组件。
   */
  removeComponentAll() {
    for (const component of this._components.values()) {
      // @ts-expect-error
      component._enable = false;
      // @ts-expect-error
      component.onDisable?.();
      // @ts-expect-error
      component.onDestroy?.();
    }
    this._components.clear();
    this._needsRefreshCache = true;
  }

  /**
   * 移除 Node 及其所有组件，并清理相关资源。
   */
  destroy() {
    this.removeComponentAll();
    this.enable = false;
    this._performanceMonitor.performanceData.clear();
    registryEntityNodeByEntity.delete(this._entity);
    registryEntityNodeByUUID.delete(this._uuid);
    this.removeAll();
  }
}
